Skip to content

33.RAF 和 RIC 是什么

requestAnimationFrame:

它既不是微任务也不属于宏任务 告诉浏览器在下次重绘之前执行传入的回调函数(通常是操纵 dom,更新动画的函数);由于是每帧执行一次,那结果就是每秒的执行次数与浏览器屏幕刷新次数一样,通常是每秒 60 次。

js
let frame = 0;
let lastTime = Date.now();
const timerange = 10000; // FPS统计打点时间间隔
let rAFID = 0;
const performance = () => {
  frame++;
  const now = Date.now();
  if (now - lastTime > timerange) {
    const fps = Math.round((frame * 1000) / (now - lastTime));
    console.log(`[rAF] 10s内Frames: ${frame}, FPS:${fps}`);
    frame = 0;
    lastTime = now;
  }
  rAFID = rAF();
};

export const rAF = () => (rAFID = requestAnimationFrame(performance));
export const cAF = () => cancelAnimationFrame(rAFID);

requestIdleCallback

会在浏览器空闲时间执行回调,也就是允许开发人员在主事件循环中执行低优先级任务,而不影响一些延迟关键事件。如果有多个回调,会按照先进先出原则执行,但是当传入了 timeout,为了避免超时,有可能会打乱这个顺序

  • 它是:实验 API, 不重要优先级不高 每帧渲染后还有时间,则可以执行

  • 可以做:数据上报

js
requestIdleCallback(myNonEssentialWork, { timeout: 2000 });

function myNonEssentialWork(deadline) {
  // 当回调函数是由于超时才得以执行的话,deadline.didTimeout为true 用户就有可能感受到卡顿, 因为一帧的时间已经超过 16ms 了.
  // 核心是一个循环,只要有剩余时间,或者已经到到 timeout 期限,且列表中有任务就会一直持续
  while (
    (deadline.timeRemaining() > 0 || deadline.didTimeout) &&
    tasks.length > 0
  ) {
    doWorkIfNeeded();
  }
  if (tasks.length > 0) {
    taskHandle = requestIdleCallback(myNonEssentialWork);
  } else {
    taskHandle = 0;
  }
}

不支持模拟实现

js
const MyRequestIdleCallback = (callback) => {
  let start = Date.now();
  const timer = setTimeout(() => {
    callback({
      didTimeout: false,
      timeRemaining: () => {
        return Math.max(0, 50 - (Date.now() - start));
      },
    });
  });
  return timer;
};
window.requestIdleCallback =
  window.requestIdleCallback || MyRequestIdleCallback;

加载 资源

js
export const idleLoadResource = (files: string[], loadMethod: LoadMethod) => {
  let asyncLoadSuccess = true;
  const tasks = (dealline: DealLine) => {
    const length = files.length;
    if (dealline.timeRemaining() > idleTime && length > 0 && asyncLoadSuccess) {
      console.log(
        "[idle use]: 空闲加载资源, 空闲时间为: ",
        dealline.timeRemaining()
      );
      // load one task
      const file = files.pop();
      if (file) {
        asyncLoadSuccess = false;
        loadMethod(file)
          .then((res: any) => {
            asyncLoadSuccess = true;
          })
          .catch((err: any) => {
            console.log(
              `[idle use]: 加载错误,取消该资源的预加载 ${file}`,
              JSON.stringify(err)
            );
            asyncLoadSuccess = true;
          });
      }
    }

    if (length > 0) {
      requestIdleCallback(tasks);
    }
  };

  requestIdleCallback(tasks);
};
js
idleLoadResource(["not-answer", "keep-trying"], (file) => {
  const show = import(
    /* webpackChunkName: "lottie-[request]-show" */
    `./assets/lottie-path/${file}/show.json`
  );
  const loop = import(
    /* webpackChunkName: "lottie-[request]-loop" */
    `./assets/lottie-path/${file}/loop.json`
  );

  return Promise.all([show, loop]);
});

在 MIT 许可下发布